package org.ysh.demo.config;

import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.cache.MemoryConstrainedCacheManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.realm.jdbc.JdbcRealm;
import org.apache.shiro.realm.text.IniRealm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.filter.authc.LogoutFilter;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.config.MethodInvokingFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;
import java.util.HashMap;
import java.util.Map;

@Configuration
public class ShiroConfig {

    /**
     * 基于表单的身份验证过滤器
     *
     * @return
     */
//    @Bean
    public FormAuthenticationFilter formAuthenticationFilter() {
        FormAuthenticationFilter filter = new FormAuthenticationFilter();
        filter.setLoginUrl("/login");
        return filter;
    }

    /**
     * 退出登录Filter，注意这里不能注册为Bean，它会导致被Spring容器注册为系统Filter，这会导致所有的请求都会经过该Filter，从而引发重定向的问题
     * @return
     */
//    @Bean
    public LogoutFilter logoutFilter() {
        LogoutFilter filter = new LogoutFilter();
        filter.setRedirectUrl("/login");
        return filter;
    }

    /**
     * 测试基于Ini配置文件来验证用户及权限
     *
     * @return
     */
    @Bean
    public Realm iniRealm() {
        IniRealm realm = new IniRealm();
        realm.setResourcePath("classpath:shiro.ini");
        realm.init();
        return realm;
    }

    /**
     * 配置Shiro的缓存管理器
     * @return
     */
    @Bean
    public CacheManager cacheManager() {
        return new MemoryConstrainedCacheManager();
    }


    /**
     * 配置SecurityManager，这里基于Web环境的，所以实现类为DefaultWebSecurityManager
     *
     * @return
     */
    @Bean
    public SecurityManager securityManager(Realm iniRealm,CacheManager cacheManager) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(iniRealm);
        securityManager.setCacheManager(cacheManager);
        return securityManager;
    }

    /**
     * 这个相当于执行了 SecurityUtils.setSecurityManager(securityManager)
     *
     * @param securityManager
     * @return
     */
    @Bean
    public MethodInvokingFactoryBean setSecurityManager(SecurityManager securityManager) {
        MethodInvokingFactoryBean methodInvokingFactoryBean = new MethodInvokingFactoryBean();
        methodInvokingFactoryBean.setStaticMethod("org.apache.shiro.SecurityUtils.setSecurityManager");
        methodInvokingFactoryBean.setArguments(securityManager);
        return methodInvokingFactoryBean;
    }


    /**
     * Web应用中,Shiro可控制的Web请求必须经过Shiro主过滤器的拦截,Shiro对基于Spring的Web应用提供了完美的支持
     *
     * @return
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        bean.setSecurityManager(securityManager);

        Map<String, Filter> filters = new HashMap<>();
        filters.put("authc", formAuthenticationFilter());
        filters.put("logout", logoutFilter());
        bean.setFilters(filters);

        // 定义Shiro过滤链
        // authc：该过滤器下的页面必须验证后才能访问,它是Shiro内置的一个拦截器org.apache.shiro.web.filter.authc.FormAuthenticationFilter
        // anon：它对应的过滤器里面是空的,什么都没做
        Map<String, String> filterChainMap = new HashMap<>();
        filterChainMap.put("/layui/**", "anon");
        filterChainMap.put("/crypto/**", "anon");
        filterChainMap.put("/archive/**", "anon");
        filterChainMap.put("/download/**", "anon");
        filterChainMap.put("/login", "authc");
        filterChainMap.put("/logout", "logout");
        filterChainMap.put("/**", "user");//这里可以配合remeberMe去使用，用户不一定需要已经通过认证,只需要曾经被Shiro记住过登录状态就可以正常发起'/home'请求
        bean.setFilterChainDefinitionMap(filterChainMap);

        bean.setLoginUrl("/login");
        bean.setSuccessUrl("/index");
        bean.setUnauthorizedUrl("/unauthorized");
        return bean;
    }


}
